1 module d_snprintf.test;
2 
3 // version = SNPRINTF_TEST;
4 
5 version (SNPRINTF_TEST) {
6 
7 import d_snprintf;
8 
9 import core.stdc.math : pow;
10 import core.stdc.string : strcmp;
11 import core.stdc.stdlib : malloc;
12 import core.stdc.stdio : sprintf, FILE, fwrite;
13 
14 nothrow:
15 @nogc:
16 
17 version(D_BetterC) {
18     version (Windows) {
19         // Workaround for https://issues.dlang.org/show_bug.cgi?id=18816 and https://issues.dlang.org/show_bug.cgi?id=19933
20         private extern extern(C) FILE* __acrt_iob_func(int);
21         shared FILE* stdout;
22     } else {
23         import core.stdc.stdio : stdout;
24     }
25 } else {
26     import core.stdc.stdio : stdout;
27 }
28 
29 enum INT_MIN = int.min;
30 enum INT_MAX = int.max;
31 enum LONG_MIN = long.max;
32 enum LONG_MAX = long.min;
33 enum UINT_MAX = uint.max;
34 
35 // wrappers for file writing and memory allocation
36 void write_file(void* p_file, ubyte[] data) {
37     fwrite(data.ptr, 1, data.length, cast(FILE*)p_file);
38 }
39 
40 void* alloc_func(size_t size) {
41     return malloc(size);
42 }
43 
44 // Definitions of printf, fprintf and asprintf based on our wrappers
45 alias asprintf = rpl_asprintf!alloc_func;
46 alias vasprintf = rpl_vasprintf!alloc_func;
47 
48 int fprintf(A...)(FILE* file, string format, A a) {
49     mixin va_start!a;
50     return rpl_vfprintf!write_file(cast(void*)file, format, va_args);
51 }
52 
53 int vfprintf(FILE* file, string format, va_list ap) {
54     return rpl_vfprintf!write_file(cast(void*)file, format, ap);
55 }
56 
57 int printf(A...)(string format, A a) {
58     mixin va_start!a;
59     return rpl_vfprintf!write_file(cast(void*)stdout, format, va_args);
60 }
61 
62 int vprintf(string format, va_list ap) {
63     return rpl_vfprintf!write_file(cast(void*)stdout, format, ap);
64 }
65 
66 // Programm to test this version of snprintf against the stdio version
67 // NOTE: Their will always be differences due to
68 //       - floating-point accuracy (e.q. 9.899999895424116 vs 9.899999895424115)
69 //       - varying feature support (e.q. no support for the ' prefix on Windows)
70 //       - "implementation dependent" formats (e.q. pointers, 00007FF666AECBC0 vs 0x7ff666aecbc0)
71 version(D_BetterC) {
72     extern(C) int main() {
73         version (Windows) {
74             // Workaround for https://issues.dlang.org/show_bug.cgi?id=18816 and https://issues.dlang.org/show_bug.cgi?id=19933
75             stdout = __acrt_iob_func(1);
76         }
77         return test();
78     }
79 } else {
80     int main() {
81         return test();
82     }
83 }
84 
85 int test() {
86     __gshared const string[] float_fmt = [
87         /* "%E" and "%e" formats. */
88         "%.16e",
89         "%22.16e",
90         "%022.16e",
91         "%-22.16e",
92         "%#+'022.16e",
93         "foo|%#+0123.9E|bar",
94         "%-123.9e",
95         "%123.9e",
96         "%+23.9e",
97         "%+05.8e",
98         "%-05.8e",
99         "%05.8e",
100         "%+5.8e",
101         "%-5.8e",
102         "% 5.8e",
103         "%5.8e",
104         "%+4.9e",
105         "%+#010.0e",
106         "%#10.1e",
107         "%10.5e",
108         "% 10.5e",
109         "%5.0e",
110         "%5.e",
111         "%#5.0e",
112         "%#5.e",
113         "%3.2e",
114         "%3.1e",
115         "%-1.5e",
116         "%1.5e",
117         "%01.3e",
118         "%1.e",
119         "%.1e",
120         "%#.0e",
121         "%+.0e",
122         "% .0e",
123         "%.0e",
124         "%#.e",
125         "%+.e",
126         "% .e",
127         "%.e",
128         "%4e",
129         "%e",
130         "%E",
131         /* "%F" and "%f" formats. */
132         "% '022f",
133         "%+'022f",
134         "%-'22f",
135         "%'22f",
136         "%.16f",
137         "%22.16f",
138         "%022.16f",
139         "%-22.16f",
140         "%#+'022.16f",
141         "foo|%#+0123.9F|bar",
142         "%-123.9f",
143         "%123.9f",
144         "%+23.9f",
145         "%+#010.0f",
146         "%#10.1f",
147         "%10.5f",
148         "% 10.5f",
149         "%+05.8f",
150         "%-05.8f",
151         "%05.8f",
152         "%+5.8f",
153         "%-5.8f",
154         "% 5.8f",
155         "%5.8f",
156         "%5.0f",
157         "%5.f",
158         "%#5.0f",
159         "%#5.f",
160         "%+4.9f",
161         "%3.2f",
162         "%3.1f",
163         "%-1.5f",
164         "%1.5f",
165         "%01.3f",
166         "%1.f",
167         "%.1f",
168         "%#.0f",
169         "%+.0f",
170         "% .0f",
171         "%.0f",
172         "%#.f",
173         "%+.f",
174         "% .f",
175         "%.f",
176         "%4f",
177         "%f",
178         "%F",
179         /* "%G" and "%g" formats. */
180         "% '022g",
181         "%+'022g",
182         "%-'22g",
183         "%'22g",
184         "%.16g",
185         "%22.16g",
186         "%022.16g",
187         "%-22.16g",
188         "%#+'022.16g",
189         "foo|%#+0123.9G|bar",
190         "%-123.9g",
191         "%123.9g",
192         "%+23.9g",
193         "%+05.8g",
194         "%-05.8g",
195         "%05.8g",
196         "%+5.8g",
197         "%-5.8g",
198         "% 5.8g",
199         "%5.8g",
200         "%+4.9g",
201         "%+#010.0g",
202         "%#10.1g",
203         "%10.5g",
204         "% 10.5g",
205         "%5.0g",
206         "%5.g",
207         "%#5.0g",
208         "%#5.g",
209         "%3.2g",
210         "%3.1g",
211         "%-1.5g",
212         "%1.5g",
213         "%01.3g",
214         "%1.g",
215         "%.1g",
216         "%#.0g",
217         "%+.0g",
218         "% .0g",
219         "%.0g",
220         "%#.g",
221         "%+.g",
222         "% .g",
223         "%.g",
224         "%4g",
225         "%g",
226         "%G",
227     ];
228     __gshared const float[] float_val = [
229         -4.136,
230         -134.52,
231         -5.04030201,
232         -3410.01234,
233         -999999.999999,
234         -913450.29876,
235         -913450.2,
236         -91345.2,
237         -9134.2,
238         -913.2,
239         -91.2,
240         -9.2,
241         -9.9,
242         4.136,
243         134.52,
244         5.04030201,
245         3410.01234,
246         999999.999999,
247         913450.29876,
248         913450.2,
249         91345.2,
250         9134.2,
251         913.2,
252         91.2,
253         9.2,
254         9.9,
255         9.96,
256         9.996,
257         9.9996,
258         9.99996,
259         9.999996,
260         9.9999996,
261         9.99999996,
262         0.99999996,
263         0.99999999,
264         0.09999999,
265         0.00999999,
266         0.00099999,
267         0.00009999,
268         0.00000999,
269         0.00000099,
270         0.00000009,
271         0.00000001,
272         0.0000001,
273         0.000001,
274         0.00001,
275         0.0001,
276         0.001,
277         0.01,
278         0.1,
279         1.0,
280         1.5,
281         -1.5,
282         -1.0,
283         -0.1,
284         float.infinity,
285         -float.infinity,
286         float.nan,
287         0
288     ];
289     __gshared const string[] long_fmt = [
290         "foo|%0123ld|bar",
291         "% '0123ld",
292         "%+'0123ld",
293         "%-'123ld",
294         "%'123ld",
295         "%123.9ld",
296         "% 123.9ld",
297         "%+123.9ld",
298         "%-123.9ld",
299         "%0123ld",
300         "% 0123ld",
301         "%+0123ld",
302         "%-0123ld",
303         "%10.5ld",
304         "% 10.5ld",
305         "%+10.5ld",
306         "%-10.5ld",
307         "%010ld",
308         "% 010ld",
309         "%+010ld",
310         "%-010ld",
311         "%4.2ld",
312         "% 4.2ld",
313         "%+4.2ld",
314         "%-4.2ld",
315         "%04ld",
316         "% 04ld",
317         "%+04ld",
318         "%-04ld",
319         "%5.5ld",
320         "%+22.33ld",
321         "%01.3ld",
322         "%1.5ld",
323         "%-1.5ld",
324         "%44ld",
325         "%4ld",
326         "%4.0ld",
327         "%4.ld",
328         "%.44ld",
329         "%.4ld",
330         "%.0ld",
331         "%.ld",
332         "%ld",
333     ];
334     __gshared const int[] long_val = [
335         INT_MAX,
336         INT_MIN,
337         - 91340,
338         91340,
339         341,
340         134,
341         131,
342         -1,
343         1,
344         0
345     ];
346     __gshared const string[] ulong_fmt = [
347         /* "%u" formats. */
348         "foo|%0123lu|bar",
349         "% '0123lu",
350         "%+'0123lu",
351         "%-'123lu",
352         "%'123lu",
353         "%123.9lu",
354         "% 123.9lu",
355         "%+123.9lu",
356         "%-123.9lu",
357         "%0123lu",
358         "% 0123lu",
359         "%+0123lu",
360         "%-0123lu",
361         "%5.5lu",
362         "%+22.33lu",
363         "%01.3lu",
364         "%1.5lu",
365         "%-1.5lu",
366         "%44lu",
367         "%lu",
368         /* "%o" formats. */
369         "foo|%#0123lo|bar",
370         "%#123.9lo",
371         "%# 123.9lo",
372         "%#+123.9lo",
373         "%#-123.9lo",
374         "%#0123lo",
375         "%# 0123lo",
376         "%#+0123lo",
377         "%#-0123lo",
378         "%#5.5lo",
379         "%#+22.33lo",
380         "%#01.3lo",
381         "%#1.5lo",
382         "%#-1.5lo",
383         "%#44lo",
384         "%#lo",
385         "%123.9lo",
386         "% 123.9lo",
387         "%+123.9lo",
388         "%-123.9lo",
389         "%0123lo",
390         "% 0123lo",
391         "%+0123lo",
392         "%-0123lo",
393         "%5.5lo",
394         "%+22.33lo",
395         "%01.3lo",
396         "%1.5lo",
397         "%-1.5lo",
398         "%44lo",
399         "%lo",
400         /* "%X" and "%x" formats. */
401         "foo|%#0123lX|bar",
402         "%#123.9lx",
403         "%# 123.9lx",
404         "%#+123.9lx",
405         "%#-123.9lx",
406         "%#0123lx",
407         "%# 0123lx",
408         "%#+0123lx",
409         "%#-0123lx",
410         "%#5.5lx",
411         "%#+22.33lx",
412         "%#01.3lx",
413         "%#1.5lx",
414         "%#-1.5lx",
415         "%#44lx",
416         "%#lx",
417         "%#lX",
418         "%123.9lx",
419         "% 123.9lx",
420         "%+123.9lx",
421         "%-123.9lx",
422         "%0123lx",
423         "% 0123lx",
424         "%+0123lx",
425         "%-0123lx",
426         "%5.5lx",
427         "%+22.33lx",
428         "%01.3lx",
429         "%1.5lx",
430         "%-1.5lx",
431         "%44lx",
432         "%lx",
433         "%lX",
434     ];
435     __gshared const uint[] ulong_val = [
436         UINT_MAX,
437         91340,
438         341,
439         134,
440         131,
441         1,
442         0
443     ];
444     __gshared const string[] llong_fmt = [
445         "foo|%0123lld|bar",
446         "%123.9lld",
447         "% 123.9lld",
448         "%+123.9lld",
449         "%-123.9lld",
450         "%0123lld",
451         "% 0123lld",
452         "%+0123lld",
453         "%-0123lld",
454         "%5.5lld",
455         "%+22.33lld",
456         "%01.3lld",
457         "%1.5lld",
458         "%-1.5lld",
459         "%44lld",
460         "%lld",
461     ];
462     __gshared const long[] llong_val = [
463         LONG_MAX,
464         LONG_MIN,
465         - 91340,
466         91340,
467         341,
468         134,
469         131,
470         -1,
471         1,
472         0
473     ];
474     __gshared const string[] string_fmt = [
475         "foo|%10.10s|bar",
476         "%-10.10s",
477         "%10.10s",
478         "%10.5s",
479         "%5.10s",
480         "%10.1s",
481         "%1.10s",
482         "%10.0s",
483         "%0.10s",
484         "%-42.5s",
485         "%2.s",
486         "%.10s",
487         "%.1s",
488         "%.0s",
489         "%.s",
490         "%4s",
491         "%s",
492     ];
493     __gshared const string[] string_val = [
494         "Hello",
495         "Hello, world!",
496         "Sound check: One, two, three.",
497         "This string is a little longer than the other strings.",
498         "1",
499         "\0"[0..0], // D outputs null for empty strings, this is a workaround
500         null
501     ];
502     __gshared const string[] pointer_fmt = [
503         "foo|%p|bar",
504         "%42p",
505         "%p",
506     ];
507     __gshared const string[]*[] pointer_val = [
508         &pointer_fmt,
509         &string_fmt,
510         &string_val,
511         null
512     ];
513     char[1024] buf1;
514     char[1024] buf2;
515     double digits = 9.123456789012345678901234567890123456789;
516     int failed = 0, num = 0;
517 
518     void TEST(T)(const string[] fmt, T[] val) {
519         for (int i = 0; i < fmt.length; i++)
520             for (int j = 0; j < val.length; j++) {
521                 static if (typeid(T) is typeid(const string)) {
522                     int r1 = sprintf(buf1.ptr, fmt[i].ptr, val[j].ptr);
523                 } else {
524                     int r1 = sprintf(buf1.ptr, fmt[i].ptr, val[j]);
525                 }
526                 int r2 = snprintf(buf2, fmt[i], val[j]);
527                 string a = r1 < 0 ? null : cast(string)buf1[0..r1];
528                 string b = r2 < 0 ? null : cast(string)buf2[0..r2];
529                 if (a != b || r1 != r2) {
530                     printf("Results don't match, "
531                         ~ "format string: %s\n"
532                         ~ "\t sprintf(3): [%s] (%d)\n"
533                         ~ "\tsnprintf(3): [%s] (%d)\n",
534                         fmt[i], a, r1, b, r2);
535                     failed++;
536                 }
537                 num++;
538             }
539     }
540 
541     printf("Testing our snprintf(3) against your system's sprintf(3).\n");
542     TEST(float_fmt, float_val);
543     TEST(long_fmt, long_val);
544     TEST(ulong_fmt, ulong_val);
545     TEST(llong_fmt, llong_val);
546     TEST(string_fmt, string_val);
547     TEST(pointer_fmt, pointer_val);
548     printf("Result: %d out of %d tests failed.\n", failed, num);
549 
550     printf("Checking how many digits we support: ");
551     for (int i = 0; i < 100; i++) {
552         double value = pow(10.0, cast(double)i) * digits;
553         sprintf(buf1.ptr, "%.1f\n", value);
554         snprintf(buf2, "%.1f\n", value);
555         if (strcmp(buf1.ptr, buf2.ptr) != 0) {
556             printf("apparently %d.\n", i);
557             break;
558         }
559     }
560     return (failed == 0) ? 0 : 1;
561 }
562 
563 }